﻿/*******************************************************************************
 * AAEngine
 * Copyright (c) 2010 Mike Jarosch
 *
 * Original source PushButton Engine:
 * Copyright (C) 2009 PushButton Labs, LLC
 * For more information see http://www.pushbuttonengine.com
 *
 * This file is licensed under the terms of the MIT license, which is included
 * in the "MIT License.txt" file at the root directory of this SDK.
 ******************************************************************************/

using System;
using AAEngine.Engine.Debug;
using AAEngine.Engine.Entities;

namespace AAEngine.Engine.Serialization
{
    public class ReferenceNote
    {
        private bool _reportedMissing;

        public bool Resolve()
        {
            // Look up by name.
            if (!String.IsNullOrEmpty(NameReference))
            {
                IEntity namedObject = AAGame.NameManager.Lookup(NameReference) as IEntity;

                if (namedObject == null)
                {
                    return false;
                }

                TypeUtility.SetProperty(Owner, FieldName, namedObject);
                ReportSuccess();

                return true;
            }

            // Look up a component on a named object by name (first) or type (second).
            if (!String.IsNullOrEmpty(ComponentReference))
            {
                IEntity componentObject = AAGame.NameManager.Lookup(ComponentReference) as IEntity;

                if (componentObject == null)
                {
                    return false;
                }

                IEntityComponent component = null;
                if (!String.IsNullOrEmpty(ComponentName))
                {
                    component = componentObject.LookupComponentByName(ComponentName);
                    if (component == null)
                    {
                        return false;
                    }
                }
                else
                {
                    string componentType = TypeUtility.GetFieldType(Owner, FieldName);
                    component = componentObject.LookupComponentByType(TypeUtility.GetTypeFromName(componentType));
                    if (component == null)
                    {
                        return false;
                    }
                }

                TypeUtility.SetProperty(Owner, FieldName, component);
                ReportSuccess();

                return true;
            }

            // Component reference on the entity being deserialized when the reference was created.
            if (!String.IsNullOrEmpty(ComponentName))
            {
                IEntityComponent component = CurrentEntity.LookupComponentByName(ComponentName);
                if (component == null)
                {
                    return false;
                }

                TypeUtility.SetProperty(Owner, FieldName, component);
                ReportSuccess();

                return true;
            }

            // Or instantiate a new entity.
            if (!String.IsNullOrEmpty(ObjectReference))
            {
                TypeUtility.SetProperty(Owner, FieldName, AAGame.TemplateManager.InstantiateEntity(ObjectReference));
                ReportSuccess();

                return true;
            }

            // Nope, none of the above!
            return false;
        }

        /// <summary>
        /// Trigger a console report about any references that haven't been resolved.
        /// </summary>
        public void ReportMissing()
        {
            // Don't spam.
            if (_reportedMissing)
            {
                return;
            }

            _reportedMissing = true;

            string firstPart = Owner.ToString() + "[" + FieldName + "] on entity '" + CurrentEntity.Name + "' - ";

            // Name reference
            if (!String.IsNullOrEmpty(NameReference))
            {
                Logger.Warn(this, "ReportMissing", firstPart + "Couldn't resolve reference to named entity '" + NameReference + "'.");
                return;
            }

            // Look up a component on a named object by name (first) or type (second).
            if (!String.IsNullOrEmpty(ComponentReference))
            {
                Logger.Warn(this, "ReportMissing", firstPart + "Couldn't find named entity '" + ComponentReference + "'.");
                return;
            }

            // Component reference on the entity being deserialized when the reference was created.
            if (!String.IsNullOrEmpty(ComponentName))
            {
                Logger.Warn(this, "ReportMissing", firstPart + "Couldn't find component on same entity named '" + ComponentName + "'.");
                return;
            }
        }

        /// <summary>
        /// Trigger a console report about any references that have been resolved.
        /// </summary>
        public void ReportSuccess()
        {
            // If we succeeded with no spam then be quiet on success too.
            if (_reportedMissing)
            {
                return;
            }
            _reportedMissing = true;

            string firstPart = Owner.ToString() + "[" + FieldName + "] on entity '" + CurrentEntity.Name + "' - ";

            // Name reference
            if (!String.IsNullOrEmpty(NameReference))
            {
                Logger.Warn(this, "ReportSuccess", firstPart + "After failure, was able to resolve reference to named entity '" + NameReference + "'.");
                return;
            }

            // Look up a component on a named object by name (first) or type (second).
            if (!String.IsNullOrEmpty(ComponentReference))
            {
                Logger.Warn(this, "ReportSuccess", firstPart + "After failure, was able to find named entity '" + ComponentReference + "'.");
                return;
            }

            // Component reference on the entity being deserialized when the reference was created.
            if (!String.IsNullOrEmpty(ComponentName))
            {
                Logger.Warn(this, "ReportSuccess", firstPart + "After failure, was able to find component on same entity named '" + ComponentName + "'.");
                return;
            }
        }

        public object Owner { get; set; }

        public string FieldName { get; set; }

        public string NameReference { get; set; }

        public string ComponentReference { get; set; }

        public string ComponentName { get; set; }

        public string ObjectReference { get; set; }

        public IEntity CurrentEntity { get; set; }
    }
}
